#pragma once
#include "MathUtils.h"
namespace Alice{
    struct FLinearColor;
    /**
     * A vector in 3-D space composed of components (X, Y, Z) with floating point precision.
     */
    template<typename T>
    struct TVector
    {
        static_assert(std::is_floating_point_v<T>, "T must be floating point");

    public:
        using FReal = T;

        union
        {
            struct
            {
                /** Vector's X component. */
                T X;

                /** Vector's Y component. */
                T Y;

                /** Vector's Z component. */
                T Z;
            };
            T XYZ[3];
        };

        /** A zero vector (0,0,0) */
        static const TVector<T> ZeroVector;
        
        /** One vector (1,1,1) */
        static const TVector<T> OneVector;
        
        /** Unreal up vector (0,0,1) */
        static const TVector<T> UpVector;
        
        /** Unreal down vector (0,0,-1) */
        static const TVector<T> DownVector;
        
        /** Unreal forward vector (1,0,0) */
        static const TVector<T> ForwardVector;
        
        /** Unreal backward vector (-1,0,0) */
        static const TVector<T> BackwardVector;
        
        /** Unreal right vector (0,1,0) */
        static const TVector<T> RightVector;
        
        /** Unreal left vector (0,-1,0) */
        static const TVector<T> LeftVector;
        
        /** Unit X axis vector (1,0,0) */
        static const TVector<T> XAxisVector;
        
        /** Unit Y axis vector (0,1,0) */
        static const TVector<T> YAxisVector;
        
        /** Unit Z axis vector (0,0,1) */
        static const TVector<T> ZAxisVector;

        /** @return Zero Vector (0,0,0) */
        static inline TVector<T> Zero() { return ZeroVector; }

        /** @return One Vector (1,1,1) */
        static inline TVector<T> One() { return OneVector; }

        /** @return Unit X Vector (1,0,0)  */
        static inline TVector<T> UnitX() { return XAxisVector; }

        /** @return Unit Y Vector (0,1,0)  */
        static inline TVector<T> UnitY() { return YAxisVector; }

        /** @return Unit Z Vector (0,0,1)  */
        static inline TVector<T> UnitZ() { return ZAxisVector; }

    public:

    #if ENABLE_NAN_DIAGNOSTIC
        FORCEINLINE void DiagnosticCheckNaN() const
        {
            if (ContainsNaN())
            {
                logOrEnsureNanError(TEXT("FVector contains NaN: %s"), *ToString());
                *const_cast<TVector<T>*>(static_cast<const TVector<T>*>(this)) = ZeroVector;
            }
        }

        FORCEINLINE void DiagnosticCheckNaN(const TCHAR* Message) const
        {
            if (ContainsNaN())
            {
                logOrEnsureNanError(TEXT("%s: FVector contains NaN: %s"), Message, *ToString());
                *const_cast<TVector<T>*>(static_cast<const TVector<T>*>(this)) = ZeroVector;
            }
        }
    #else
        FORCEINLINE void DiagnosticCheckNaN() const {}
        FORCEINLINE void DiagnosticCheckNaN(const TCHAR* Message) const {}
    #endif

        /** Default constructor (no initialization). */
        TVector() = default;

        /**
         * Constructor initializing all components to a single T value.
         *
         * @param InF Value to set all components to.
         */
        explicit FORCEINLINE TVector(T InF);

        /**
         * Constructor using initial values for each component.
         *
         * @param InX X Coordinate.
         * @param InY Y Coordinate.
         * @param InZ Z Coordinate.
         */
        FORCEINLINE TVector(T InX, T InY, T InZ);

        /**
         * Constructs a vector from an FLinearColor.
         *
         * @param InColor Color to copy from.
         */
        explicit TVector(const FLinearColor& InColor);

        /**
         * Constructor which initializes all components to zero.
         *
         * @param EForceInit Force init enum
         */
        explicit FORCEINLINE TVector(EForceInit);

        /**
         * Calculate cross product between this and another vector.
         *
         * @param V The other vector.
         * @return The cross product.
         */
        FORCEINLINE TVector<T> operator^(const TVector<T>& V) const;

        /**
         * Calculate cross product between this and another vector.
         *
         * @param V The other vector.
         * @return The cross product.
         */
        FORCEINLINE TVector<T> Cross(const TVector<T>& V2) const;

        /**
         * Calculate the cross product of two vectors.
         *
         * @param A The first vector.
         * @param B The second vector.
         * @return The cross product.
         */
        FORCEINLINE static TVector<T> CrossProduct(const TVector<T>& A, const TVector<T>& B);

        /**
         * Calculate the dot product between this and another vector.
         *
         * @param V The other vector.
         * @return The dot product.
         */
        FORCEINLINE T operator|(const TVector<T>& V) const;

        /**
         * Calculate the dot product between this and another vector.
         *
         * @param V The other vector.
         * @return The dot product.
         */
        FORCEINLINE T Dot(const TVector<T>& V) const;

        /**
         * Calculate the dot product of two vectors.
         *
         * @param A The first vector.
         * @param B The second vector.
         * @return The dot product.
         */
        FORCEINLINE static T DotProduct(const TVector<T>& A, const TVector<T>& B);

        /**
         * Gets the result of component-wise addition of this and another vector.
         *
         * @param V The vector to add to this.
         * @return The result of vector addition.
         */
        FORCEINLINE TVector<T> operator+(const TVector<T>& V) const;

        /**
         * Gets the result of component-wise subtraction of this by another vector.
         *
         * @param V The vector to subtract from this.
         * @return The result of vector subtraction.
         */
        FORCEINLINE TVector<T> operator-(const TVector<T>& V) const;

        /**
         * Gets the result of subtracting from each component of the vector.
         *
         * @param Bias How much to subtract from each component.
         * @return The result of subtraction.
         */
        template<typename FArg>
        FORCEINLINE TVector<T> operator-(FArg Bias) const
        {
            return TVector<T>(X - (T)Bias, Y - (T)Bias, Z - (T)Bias);
        }

        /**
         * Gets the result of adding to each component of the vector.
         *
         * @param Bias How much to add to each component.
         * @return The result of addition.
         */
        template<typename FArg>
        FORCEINLINE TVector<T> operator+(FArg Bias) const
        {
            return TVector<T>(X + (T)Bias, Y + (T)Bias, Z + (T)Bias);
        }

        /**
         * Gets the result of scaling the vector (multiplying each component by a value).
         *
         * @param Scale What to multiply each component by.
         * @return The result of multiplication.
         */
        template<typename FArg>
        FORCEINLINE TVector<T> operator*(FArg Scale) const
        {
            return TVector<T>(X * (T)Scale, Y * (T)Scale, Z * (T)Scale);
        }

        /**
         * Gets the result of dividing each component of the vector by a value.
         *
         * @param Scale What to divide each component by.
         * @return The result of division.
         */
        template<typename FArg>
        TVector<T> operator/(FArg Scale) const
        {
            const T RScale = T(1) / Scale;
            return TVector<T>(X * RScale, Y * RScale, Z * RScale);
        }

        /**
         * Gets the result of component-wise multiplication of this vector by another.
         *
         * @param V The vector to multiply with.
         * @return The result of multiplication.
         */
        FORCEINLINE TVector<T> operator*(const TVector<T>& V) const;

        /**
         * Gets the result of component-wise division of this vector by another.
         *
         * @param V The vector to divide by.
         * @return The result of division.
         */
        FORCEINLINE TVector<T> operator/(const TVector<T>& V) const;

        // Binary comparison operators.

        /**
         * Check against another vector for equality.
         *
         * @param V The vector to check against.
         * @return true if the vectors are equal, false otherwise.
         */
        bool operator==(const TVector<T>& V) const;

        /**
         * Check against another vector for inequality.
         *
         * @param V The vector to check against.
         * @return true if the vectors are not equal, false otherwise.
         */
        bool operator!=(const TVector<T>& V) const;

        /**
         * Check against another vector for equality, within specified error limits.
         *
         * @param V The vector to check against.
         * @param Tolerance Error tolerance.
         * @return true if the vectors are equal within tolerance limits, false otherwise.
         */
        bool Equals(const TVector<T>& V, T Tolerance=UE_KINDA_SMALL_NUMBER) const;

        /**
         * Checks whether all components of this vector are the same, within a tolerance.
         *
         * @param Tolerance Error tolerance.
         * @return true if the vectors are equal within tolerance limits, false otherwise.
         */
        bool AllComponentsEqual(T Tolerance=UE_KINDA_SMALL_NUMBER) const;

        /**
         * Get a negated copy of the vector.
         *
         * @return A negated copy of the vector.
         */
        FORCEINLINE TVector<T> operator-() const;

        /**
         * Adds another vector to this.
         * Uses component-wise addition.
         *
         * @param V Vector to add to this.
         * @return Copy of the vector after addition.
         */
        FORCEINLINE TVector<T> operator+=(const TVector<T>& V);

        /**
         * Subtracts another vector from this.
         * Uses component-wise subtraction.
         *
         * @param V Vector to subtract from this.
         * @return Copy of the vector after subtraction.
         */
        FORCEINLINE TVector<T> operator-=(const TVector<T>& V);

        /**
         * Scales the vector.
         *
         * @param Scale Amount to scale this vector by.
         * @return Copy of the vector after scaling.
         */
        template<typename FArg>
        FORCEINLINE TVector<T> operator*=(FArg Scale)
        {
            X *= Scale; Y *= Scale; Z *= Scale;
            DiagnosticCheckNaN();
            return *this;
        }

        /**
         * Divides the vector by a number.
         *
         * @param V What to divide this vector by.
         * @return Copy of the vector after division.
         */
        template<typename FArg>
        TVector<T> operator/=(FArg Scale)
        {
            const T RV = (T)1 / Scale;
            X *= RV; Y *= RV; Z *= RV;
            DiagnosticCheckNaN();
            return *this;
        }

        /**
         * Multiplies the vector with another vector, using component-wise multiplication.
         *
         * @param V What to multiply this vector with.
         * @return Copy of the vector after multiplication.
         */
        TVector<T> operator*=(const TVector<T>& V);

        /**
         * Divides the vector by another vector, using component-wise division.
         *
         * @param V What to divide vector by.
         * @return Copy of the vector after division.
         */
        TVector<T> operator/=(const TVector<T>& V);

        /**
         * Gets specific component of the vector.
         *
         * @param Index the index of vector component
         * @return reference to component.
         */
        T& operator[](int32 Index);

        /**
         * Gets specific component of the vector.
         *
         * @param Index the index of vector component
         * @return Copy of the component.
         */
        T operator[](int32 Index)const;

        /**
        * Gets a specific component of the vector.
        *
        * @param Index The index of the component required.
        *
        * @return Reference to the specified component.
        */
        T& Component(int32 Index);

        /**
        * Gets a specific component of the vector.
        *
        * @param Index The index of the component required.
        * @return Copy of the specified component.
        */
        T Component(int32 Index) const;

    public:

        // Simple functions.

        /**
         * Set the values of the vector directly.
         *
         * @param InX New X coordinate.
         * @param InY New Y coordinate.
         * @param InZ New Z coordinate.
         */
        void Set(T InX, T InY, T InZ);

        /**
         * Get the maximum value of the vector's components.
         *
         * @return The maximum value of the vector's components.
         */
        T GetMax() const;

        /**
         * Get the maximum absolute value of the vector's components.
         *
         * @return The maximum absolute value of the vector's components.
         */
        T GetAbsMax() const;

        /**
         * Get the minimum value of the vector's components.
         *
         * @return The minimum value of the vector's components.
         */
        T GetMin() const;

        /**
         * Get the minimum absolute value of the vector's components.
         *
         * @return The minimum absolute value of the vector's components.
         */
        T GetAbsMin() const;

        /** Gets the component-wise min of two vectors. */
        TVector<T> ComponentMin(const TVector<T>& Other) const;

        /** Gets the component-wise max of two vectors. */
        TVector<T> ComponentMax(const TVector<T>& Other) const;

        /**
         * Get a copy of this vector with absolute value of each component.
         *
         * @return A copy of this vector with absolute value of each component.
         */
        TVector<T> GetAbs() const;

        /**
         * Get the length (magnitude) of this vector.
         *
         * @return The length of this vector.
         */
        T Size() const;

        /**
         * Get the length (magnitude) of this vector.
         *
         * @return The length of this vector.
         */
        T Length() const;

        /**
         * Get the squared length of this vector.
         *
         * @return The squared length of this vector.
         */
        T SizeSquared() const;

        /**
         * Get the squared length of this vector.
         *
         * @return The squared length of this vector.
         */
        T SquaredLength() const;

        /**
         * Get the length of the 2D components of this vector.
         *
         * @return The 2D length of this vector.
         */
        T Size2D() const ;

        /**
         * Get the squared length of the 2D components of this vector.
         *
         * @return The squared 2D length of this vector.
         */
        T SizeSquared2D() const ;

        /**
         * Checks whether vector is near to zero within a specified tolerance.
         *
         * @param Tolerance Error tolerance.
         * @return true if the vector is near to zero, false otherwise.
         */
        bool IsNearlyZero(T Tolerance=UE_KINDA_SMALL_NUMBER) const;

        /**
         * Checks whether all components of the vector are exactly zero.
         *
         * @return true if the vector is exactly zero, false otherwise.
         */
        bool IsZero() const;

        /**
         * Check if the vector is of unit length, with specified tolerance.
         *
         * @param LengthSquaredTolerance Tolerance against squared length.
         * @return true if the vector is a unit vector within the specified tolerance.
         */
        FORCEINLINE bool IsUnit(T LengthSquaredTolerance = UE_KINDA_SMALL_NUMBER) const;

        /**
         * Checks whether vector is normalized.
         *
         * @return true if normalized, false otherwise.
         */
        bool IsNormalized() const;

        /**
         * Normalize this vector in-place if it is larger than a given tolerance. Leaves it unchanged if not.
         *
         * @param Tolerance Minimum squared length of vector for normalization.
         * @return true if the vector was normalized correctly, false otherwise.
         */
        bool Normalize(T Tolerance=UE_SMALL_NUMBER);

        /**
         * Calculates normalized version of vector without checking for zero length.
         *
         * @return Normalized version of vector.
         * @see GetSafeNormal()
         */
        FORCEINLINE TVector<T> GetUnsafeNormal() const;

        /**
         * Gets a normalized copy of the vector, checking it is safe to do so based on the length.
         * Returns zero vector by default if vector length is too small to safely normalize.
         *
         * @param Tolerance Minimum squared vector length.
         * @return A normalized copy if safe, ResultIfZero otherwise.
         */
        TVector<T> GetSafeNormal(T Tolerance=UE_SMALL_NUMBER, const TVector<T>& ResultIfZero = ZeroVector) const;

        /**
         * Gets a normalized copy of the 2D components of the vector, checking it is safe to do so. Z is set to zero. 
         * Returns zero vector by default if vector length is too small to normalize.
         *
         * @param Tolerance Minimum squared vector length.
         * @return Normalized copy if safe, otherwise returns ResultIfZero.
         */
        TVector<T> GetSafeNormal2D(T Tolerance=UE_SMALL_NUMBER, const TVector<T>& ResultIfZero = ZeroVector) const;

        /**
         * Util to convert this vector into a unit direction vector and its original length.
         *
         * @param OutDir Reference passed in to store unit direction vector.
         * @param OutLength Reference passed in to store length of the vector.
         */
        void ToDirectionAndLength(TVector<T>& OutDir, double& OutLength) const;
        void ToDirectionAndLength(TVector<T>& OutDir, float& OutLength) const;

        /**
         * Get a copy of the vector as sign only.
         * Each component is set to +1 or -1, with the sign of zero treated as +1.
         *
         * @param A copy of the vector with each component set to +1 or -1
         */
        FORCEINLINE TVector<T> GetSignVector() const;

        /**
         * Projects 2D components of vector based on Z.
         *
         * @return Projected version of vector based on Z.
         */
        TVector<T> Projection() const;

        /**
        * Calculates normalized 2D version of vector without checking for zero length.
        *
        * @return Normalized version of vector.
        * @see GetSafeNormal2D()
        */
        FORCEINLINE TVector<T> GetUnsafeNormal2D() const;

        /**
         * Gets a copy of this vector snapped to a grid.
         *
         * @param GridSz Grid dimension.
         * @return A copy of this vector snapped to a grid.
         * @see FMath::GridSnap()
         */
        TVector<T> GridSnap(const T& GridSz) const;

        /**
         * Get a copy of this vector, clamped inside of a cube.
         *
         * @param Radius Half size of the cube.
         * @return A copy of this vector, bound by cube.
         */
        TVector<T> BoundToCube(T Radius) const;

        /** Get a copy of this vector, clamped inside of a cube. */
        TVector<T> BoundToBox(const TVector<T>& Min, const TVector<T>& Max) const;

        /** Create a copy of this vector, with its magnitude clamped between Min and Max. */
        TVector<T> GetClampedToSize(T Min, T Max) const;

        /** Create a copy of this vector, with the 2D magnitude clamped between Min and Max. Z is unchanged. */
        TVector<T> GetClampedToSize2D(T Min, T Max) const;

        /** Create a copy of this vector, with its maximum magnitude clamped to MaxSize. */
        TVector<T> GetClampedToMaxSize(T MaxSize) const;

        /** Create a copy of this vector, with the maximum 2D magnitude clamped to MaxSize. Z is unchanged. */
        TVector<T> GetClampedToMaxSize2D(T MaxSize) const;

        /**
         * Add a vector to this and clamp the result in a cube.
         *
         * @param V Vector to add.
         * @param Radius Half size of the cube.
         */
        void AddBounded(const TVector<T>& V, T Radius=MAX_int16);

        /**
         * Gets the reciprocal of this vector, avoiding division by zero.
         * Zero components are set to BIG_NUMBER.
         *
         * @return Reciprocal of this vector.
         */
        TVector<T> Reciprocal() const;

        /**
         * Check whether X, Y and Z are nearly equal.
         *
         * @param Tolerance Specified Tolerance.
         * @return true if X == Y == Z within the specified tolerance.
         */
        bool IsUniform(T Tolerance=ALICE_KINDA_SMALL_NUMBER) const;

        /**
         * Mirror a vector about a normal vector.
         *
         * @param MirrorNormal Normal vector to mirror about.
         * @return Mirrored vector.
         */
        TVector<T> MirrorByVector(const TVector<T>& MirrorNormal) const;

        /**
         * Rotates around Axis (assumes Axis.Size() == 1).
         *
         * @param AngleDeg Angle to rotate (in degrees).
         * @param Axis Axis to rotate around.
         * @return Rotated Vector.
         */
        TVector<T> RotateAngleAxis(const T AngleDeg, const TVector<T>& Axis) const;
        
        /**
         * Rotates around Axis (assumes Axis.Size() == 1).
         *
         * @param AngleRad Angle to rotate (in radians).
         * @param Axis Axis to rotate around.
         * @return Rotated Vector.
         */
        TVector<T> RotateAngleAxisRad(const T AngleRad, const TVector<T>& Axis) const;

        /**
         * Returns the cosine of the angle between this vector and another projected onto the XY plane (no Z).
         *
         * @param B the other vector to find the 2D cosine of the angle with.
         * @return The cosine.
         */
        FORCEINLINE T CosineAngle2D(TVector<T> B) const;

        /**
         * Gets a copy of this vector projected onto the input vector.
         *
         * @param A	Vector to project onto, does not assume it is normalized.
         * @return Projected vector.
         */
        FORCEINLINE TVector<T> ProjectOnTo(const TVector<T>& A) const ;

        /**
         * Gets a copy of this vector projected onto the input vector, which is assumed to be unit length.
         * 
         * @param  Normal Vector to project onto (assumed to be unit length).
         * @return Projected vector.
         */
        FORCEINLINE TVector<T> ProjectOnToNormal(const TVector<T>& Normal) const;

        /**
         * Find good arbitrary axis vectors to represent U and V axes of a plane,
         * using this vector as the normal of the plane.
         *
         * @param Axis1 Reference to first axis.
         * @param Axis2 Reference to second axis.
         */
        void FindBestAxisVectors(TVector<T>& Axis1, TVector<T>& Axis2) const;

        /** When this vector contains Euler angles (degrees), ensure that angles are between +/-180 */
        void UnwindEuler();

        /**
         * Utility to check if there are any non-finite values (NaN or Inf) in this vector.
         *
         * @return true if there are any non-finite values in this vector, false otherwise.
         */
        bool ContainsNaN() const;

        /**
         * Convert a direction vector into a 'heading' angle.
         *
         * @return 'Heading' angle between +/-PI. 0 is pointing down +X.
         */
        T HeadingAngle() const;

        /**
         * Interpolate from a vector to the direction of another vector along a spherical path.
         * 
         * @param V Vector we interpolate from
         * @param Direction Target direction we interpolate to
         * @param Alpha interpolation amount, usually between 0-1
         * @return Vector after interpolating between Vector and Direction along a spherical path. The magnitude will remain the length of the starting vector.
         */
        static TVector<T> SlerpVectorToDirection(TVector<T>& V, TVector<T>& Direction, T Alpha);

        /**
         * Interpolate from normalized vector A to normalized vector B along a spherical path.
         *
         * @param NormalA Start direction of interpolation, must be normalized.
         * @param NormalB End target direction of interpolation, must be normalized.
         * @param Alpha interpolation amount, usually between 0-1
         * @return Normalized vector after interpolating between NormalA and NormalB along a spherical path.
         */
        static TVector<T> SlerpNormals(TVector<T>& NormalA, TVector<T>& NormalB, T Alpha);

        /**
         * Create an orthonormal basis from a basis with at least two orthogonal vectors.
         * It may change the directions of the X and Y axes to make the basis orthogonal,
         * but it won'T change the direction of the Z axis.
         * All axes will be normalized.
         *
         * @param XAxis The input basis' XAxis, and upon return the orthonormal basis' XAxis.
         * @param YAxis The input basis' YAxis, and upon return the orthonormal basis' YAxis.
         * @param ZAxis The input basis' ZAxis, and upon return the orthonormal basis' ZAxis.
         */
        static void CreateOrthonormalBasis(TVector<T>& XAxis,TVector<T>& YAxis,TVector<T>& ZAxis);

        /**
         * Compare two points and see if they're the same, using a threshold.
         *
         * @param P First vector.
         * @param Q Second vector.
         * @return Whether points are the same within a threshold. Uses fast distance approximation (linear per-component distance).
         */
        static bool PointsAreSame(const TVector<T> &P, const TVector<T> &Q);
        
        /**
         * Compare two points and see if they're within specified distance.
         *
         * @param Point1 First vector.
         * @param Point2 Second vector.
         * @param Dist Specified distance.
         * @return Whether two points are within the specified distance. Uses fast distance approximation (linear per-component distance).
         */
        static bool PointsAreNear(const TVector<T> &Point1, const TVector<T> &Point2, T Dist);

        /**
         * Calculate the signed distance (in the direction of the normal) between a point and a plane.
         *
         * @param Point The Point we are checking.
         * @param PlaneBase The Base Point in the plane.
         * @param PlaneNormal The Normal of the plane (assumed to be unit length).
         * @return Signed distance between point and plane.
         */
        static T PointPlaneDist(const TVector<T> &Point, const TVector<T> &PlaneBase, const TVector<T> &PlaneNormal);


        /**
         * Calculate the projection of a point on the plane defined by counter-clockwise (CCW) points A,B,C.
         *
         * @param Point The point to project onto the plane
         * @param A 1st of three points in CCW order defining the plane 
         * @param B 2nd of three points in CCW order defining the plane
         * @param C 3rd of three points in CCW order defining the plane
         * @return Projection of Point onto plane ABC
         */
        static TVector<T> PointPlaneProject(const TVector<T>& Point, const TVector<T>& A, const TVector<T>& B, const TVector<T>& C);

        /**
        * Calculate the projection of a point on the plane defined by PlaneBase and PlaneNormal.
        *
        * @param Point The point to project onto the plane
        * @param PlaneBase Point on the plane
        * @param PlaneNorm Normal of the plane (assumed to be unit length).
        * @return Projection of Point onto plane
        */
        static TVector<T> PointPlaneProject(const TVector<T>& Point, const TVector<T>& PlaneBase, const TVector<T>& PlaneNormal);

        /**
         * Calculate the projection of a vector on the plane defined by PlaneNormal.
         * 
         * @param  V The vector to project onto the plane.
         * @param  PlaneNormal Normal of the plane (assumed to be unit length).
         * @return Projection of V onto plane.
         */
        static TVector<T> VectorPlaneProject(const TVector<T>& V, const TVector<T>& PlaneNormal);

        /**
         * Euclidean distance between two points.
         *
         * @param V1 The first point.
         * @param V2 The second point.
         * @return The distance between two points.
         */
        static FORCEINLINE T Dist(const TVector<T> &V1, const TVector<T> &V2);
        static FORCEINLINE T Distance(const TVector<T> &V1, const TVector<T> &V2) { return Dist(V1, V2); }

        /**
        * Euclidean distance between two points in the XY plane (ignoring Z).
        *
        * @param V1 The first point.
        * @param V2 The second point.
        * @return The distance between two points in the XY plane.
        */
        static FORCEINLINE T DistXY(const TVector<T> &V1, const TVector<T> &V2);
        static FORCEINLINE T Dist2D(const TVector<T> &V1, const TVector<T> &V2) { return DistXY(V1, V2); }

        /**
         * Squared distance between two points.
         *
         * @param V1 The first point.
         * @param V2 The second point.
         * @return The squared distance between two points.
         */
        static FORCEINLINE T DistSquared(const TVector<T> &V1, const TVector<T> &V2);

        /**
         * Squared distance between two points in the XY plane only.
         *	
        * @param V1 The first point.
        * @param V2 The second point.
        * @return The squared distance between two points in the XY plane
        */
        static FORCEINLINE T DistSquaredXY(const TVector<T> &V1, const TVector<T> &V2);
        static FORCEINLINE T DistSquared2D(const TVector<T> &V1, const TVector<T> &V2) { return DistSquaredXY(V1, V2); }
        
        /**
         * Compute pushout of a box from a plane.
         *
         * @param Normal The plane normal.
         * @param Size The size of the box.
         * @return Pushout required.
         */
        static FORCEINLINE T BoxPushOut(const TVector<T>& Normal, const TVector<T>& Size);

        /**
         * Min, Max, Min3, Max3 like FMath
         */
        static FORCEINLINE TVector<T> Min( const TVector<T>& A, const TVector<T>& B );
        static FORCEINLINE TVector<T> Max( const TVector<T>& A, const TVector<T>& B );

        static FORCEINLINE TVector<T> Min3( const TVector<T>& A, const TVector<T>& B, const TVector<T>& C );
        static FORCEINLINE TVector<T> Max3( const TVector<T>& A, const TVector<T>& B, const TVector<T>& C );

        /**
         * See if two normal vectors are nearly parallel, meaning the angle between them is close to 0 degrees.
         *
         * @param  Normal1 First normalized vector.
         * @param  Normal1 Second normalized vector.
         * @param  ParallelCosineThreshold Normals are parallel if absolute value of dot product (cosine of angle between them) is greater than or equal to this. For example: cos(1.0 degrees).
         * @return true if vectors are nearly parallel, false otherwise.
         */
        static bool Parallel(const TVector<T>& Normal1, const TVector<T>& Normal2, T ParallelCosineThreshold = ALICE_THRESH_NORMALS_ARE_PARALLEL);

        /**
         * See if two normal vectors are coincident (nearly parallel and point in the same direction).
         * 
         * @param  Normal1 First normalized vector.
         * @param  Normal2 Second normalized vector.
         * @param  ParallelCosineThreshold Normals are coincident if dot product (cosine of angle between them) is greater than or equal to this. For example: cos(1.0 degrees).
         * @return true if vectors are coincident (nearly parallel and point in the same direction), false otherwise.
         */
        static bool Coincident(const TVector<T>& Normal1, const TVector<T>& Normal2, T ParallelCosineThreshold = ALICE_THRESH_NORMALS_ARE_PARALLEL);

        /**
         * See if two normal vectors are nearly orthogonal (perpendicular), meaning the angle between them is close to 90 degrees.
         * 
         * @param  Normal1 First normalized vector.
         * @param  Normal2 Second normalized vector.
         * @param  OrthogonalCosineThreshold Normals are orthogonal if absolute value of dot product (cosine of angle between them) is less than or equal to this. For example: cos(89.0 degrees).
         * @return true if vectors are orthogonal (perpendicular), false otherwise.
         */
        static bool Orthogonal(const TVector<T>& Normal1, const TVector<T>& Normal2, T OrthogonalCosineThreshold = ALICE_THRESH_NORMALS_ARE_ORTHOGONAL);

        /**
         * See if two planes are coplanar. They are coplanar if the normals are nearly parallel and the planes include the same set of points.
         *
         * @param Base1 The base point in the first plane.
         * @param Normal1 The normal of the first plane.
         * @param Base2 The base point in the second plane.
         * @param Normal2 The normal of the second plane.
         * @param ParallelCosineThreshold Normals are parallel if absolute value of dot product is greater than or equal to this.
         * @return true if the planes are coplanar, false otherwise.
         */
        static bool Coplanar(const TVector<T>& Base1, const TVector<T>& Normal1, const TVector<T>& Base2, const TVector<T>& Normal2, T ParallelCosineThreshold = ALICE_THRESH_NORMALS_ARE_PARALLEL);

        /**
         * Triple product of three vectors: X dot (Y cross Z).
         *
         * @param X The first vector.
         * @param Y The second vector.
         * @param Z The third vector.
         * @return The triple product: X dot (Y cross Z).
         */
        static T Triple(const TVector<T>& X, const TVector<T>& Y, const TVector<T>& Z);

        /**
         * Generates a list of sample points on a Bezier curve defined by 2 points.
         *
         * @param ControlPoints	Array of 4 FVectors (vert1, controlpoint1, controlpoint2, vert2).
         * @param NumPoints Number of samples.
         * @param OutPoints Receives the output samples.
         * @return The path length.
         */
        static T EvaluateBezier(const TVector<T>* ControlPoints, int32 NumPoints, std::vector<TVector<T>>& OutPoints);

        /**
         * Converts a vector containing radian values to a vector containing degree values.
         *
         * @param RadVector	Vector containing radian values
         * @return Vector  containing degree values
         */
        static TVector<T> RadiansToDegrees(const TVector<T>& RadVector);

        /**
         * Converts a vector containing degree values to a vector containing radian values.
         *
         * @param DegVector	Vector containing degree values
         * @return Vector containing radian values
         */
        static TVector<T> DegreesToRadians(const TVector<T>& DegVector);

        /**
         * Given a current set of cluster centers, a set of points, iterate N times to move clusters to be central. 
         *
         * @param Clusters Reference to array of Clusters.
         * @param Points Set of points.
         * @param NumIterations Number of iterations.
         * @param NumConnectionsToBeValid Sometimes you will have long strings that come off the mass of points
         * which happen to have been chosen as Cluster starting points.  You want to be able to disregard those.
         */
        static void GenerateClusterCenters(std::vector<TVector<T>>& Clusters, const std::vector<TVector<T>>& Points, int32 NumIterations, int32 NumConnectionsToBeValid);
        
        // Conversion from other type.
        template<typename FArg>
        explicit TVector(const TVector<FArg>& From) : TVector<T>((T)From.X, (T)From.Y, (T)From.Z) {}
    };
    using Vector3f		= TVector<float>;
    using Vector3d 	    = TVector<double>;
}